package com.rd.sys.service.sys.user.impl;

import java.io.File;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import baseproj.common.exception.BaseServiceException;
import baseproj.component.log.LoggerAdapter;
import baseproj.component.log.LoggerService;

import com.rd.sys.common.base.CommonConstants;
import com.rd.sys.common.utils.CommonUtils;
import com.rd.sys.common.utils.DateTimeUtils;
import com.rd.sys.common.utils.GetWebConfig;
import com.rd.sys.common.utils.JacksonMapper;
import com.rd.sys.common.utils.cache.AnalysisCache;
import com.rd.sys.common.utils.crypto.BASE64;
import com.rd.sys.common.utils.crypto.TripleDES;
import com.rd.sys.common.utils.file.FileLockUtils;
import com.rd.sys.common.utils.file.HandlerCaller;
import com.rd.sys.dto.commmon.enums.SubActivationEnum;
import com.rd.sys.dto.sys.user.UserInfoDto;
import com.rd.sys.dto.sys.user.UserSecurityDto;
import com.rd.sys.dto.sys.user.UserSubActivationDto;
import com.rd.sys.dto.sys.user.all.UserActivationAllInOneDto;
import com.rd.sys.dto.sys.user.all.UserAllInOneDto;
import com.rd.sys.service.sys.user.UserAllInOneService;

@Service
@Transactional(rollbackFor = {Exception.class})
public class UserAllInOneServiceImpl implements UserAllInOneService {
    private final LoggerService logger = LoggerAdapter.getLoggerService(getClass());

    private static final byte[] SYNC_LOCKER = new byte[0];

    private static final String USERPATH = CommonUtils.judgeConfigPath(GetWebConfig.getValue("user.info.path"));;

    /**
     * 从用户描述文件中读取项目信息
     * 
     * @param userCode
     * @return
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public UserAllInOneDto readUserList(String userCode) throws Exception {
        UserAllInOneDto user = null;

        /** 从用户描述文件中读取出来 */
        System.out.println(USERPATH + CommonConstants.SysParamConstants.FILE_SEPARATOR + userCode);
        File file = new File(USERPATH + CommonConstants.SysParamConstants.FILE_SEPARATOR + userCode);
        if (!file.exists()) {
            throw new UserAllInOneServiceException(UserAllInOneServiceException.USER_NOT_EXSIT);
        } else {
            byte[] userBytes = FileUtils.readFileToByteArray(file);
            if (userBytes.length > 0) {
                String userJson = new String(userBytes, CommonConstants.SysParamConstants.SYS_CHARSET);
                userJson = TripleDES.decrypt2String(CommonConstants.SysParamConstants.DEFAULT_3DES_KEY,
                        BASE64.decode(userJson)); // 原始文件是3des加密的,需要解密后使用
                user = (UserAllInOneDto) JacksonMapper.json2Bean(userJson, UserAllInOneDto.class);
            } else {
                throw new UserAllInOneServiceException(UserAllInOneServiceException.USER_NOT_EXSIT);
            }
        }

        return user;
    }

    /**
     * 获取用户安全配置信息
     * 
     * @param userInfo
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(readOnly = true)
    public UserSecurityDto selectUserSecurityInfo(UserInfoDto userInfo) throws Exception {
        try {
            UserAllInOneDto user = AnalysisCache.UserAllInOneCache.get(userInfo.getUsercode());
            if (user == null) {
                return null;
            }

            UserSecurityDto userSecurity = new UserSecurityDto();
            userSecurity.setBasekey(user.getBasekey());
            userSecurity.setUpkey(user.getBasekey());
            userSecurity.setDownkey(user.getBasekey());

            return userSecurity;
        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取用户激活码信息
     * 
     * @param userCode
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(readOnly = true)
    public List<UserSubActivationDto> findUserActivation(String userCode) throws Exception {
        try {
            UserAllInOneDto user = AnalysisCache.UserAllInOneCache.get(userCode);
            if (user == null) {
                return null;
            }

            List<UserSubActivationDto> activeList = new LinkedList<UserSubActivationDto>();
            if (user.getActiveList() != null) {
                Iterator<UserActivationAllInOneDto> iterator = user.getActiveList().iterator();
                UserSubActivationDto active = null;
                UserActivationAllInOneDto activeAll = null;
                while (iterator.hasNext()) {
                    activeAll = iterator.next();
                    active = new UserSubActivationDto();
                    active.setDevicenumber(activeAll.getDevicenumber());
                    active.setMac(activeAll.getMac());
                    active.setStatus(activeAll.getStatus());
                    active.setActivationcode(activeAll.getActivationcode());

                    activeList.add(active);
                }
            }

            return activeList;
        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新激活码为已使用
     */
    @Override
    @Transactional(readOnly = true)
    public void updateActivationToUsed(String userCode, UserSubActivationDto active) throws Exception {
        synchronized (SYNC_LOCKER) { // 更新激活码方法是同步加锁定中进行

            /** 1. 找到对应更新的激活码 */
            UserAllInOneDto user = AnalysisCache.UserAllInOneCache.get(userCode);
            boolean isExist = false;

            if (user.getActiveList() != null) {
                Iterator<UserActivationAllInOneDto> iterator = user.getActiveList().iterator();
                UserActivationAllInOneDto activeAll = null;
                while (iterator.hasNext()) {
                    activeAll = iterator.next();

                    if (activeAll.getActivationcode().equals(active.getActivationcode())) {
                        activeAll.setMac(active.getMac());
                        activeAll.setStatus(Integer.valueOf(SubActivationEnum.Status.ACTIVATED.getCode()));
                        activeAll.setUpdatetime(DateTimeUtils.getNowDatetime());

                        isExist = true;
                    }
                }
            }

            if (!isExist) {
                return; // 未找到，不用更新
            }

            /** 2. 锁定写入的文件,更新 */
            File lockFile = new File(USERPATH + CommonConstants.SysParamConstants.FILE_SEPARATOR + userCode);

            Object[] array = new Object[] {userCode, user};
            HandlerCaller caller = new HandlerCaller(array) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String userCode = (String) array[0];
                    UserAllInOneDto user = (UserAllInOneDto) array[1];

                    /** 2.更新当前用户信息 */
                    String newUserJson = JacksonMapper.bean2Json(user);

                    newUserJson = BASE64.encode(TripleDES.encrypt(CommonConstants.SysParamConstants.DEFAULT_3DES_KEY,
                            newUserJson)); // 3des加密后写入

                    ByteBuffer buffer = ByteBuffer.wrap(newUserJson
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));

                    /** 3.写入信息并更新缓存 */
                    lockChannel.write(buffer); // 锁定文件本来就是待写入文件
                }
            };

            FileLockUtils.handlerByLockFile(lockFile, caller); // 调用文件锁定方式操作

        }
    }

    /**
     * 查询用户信息
     * 
     * @param userCode
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(readOnly = true)
    public UserInfoDto selectUserInfo(String userCode) throws Exception {
        try {
            UserAllInOneDto user = AnalysisCache.UserAllInOneCache.get(userCode);
            if (user == null) {
                return null;
            }
            UserInfoDto userInfo = new UserInfoDto();
            userInfo.setUsercode(user.getUsercode());
            UserSecurityDto userSecurity = this.selectUserSecurityInfo(userInfo);
            userInfo.setSecurityInfo(userSecurity);

            return userInfo;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }
    }
}
